// Copyright (C) Kumo inc. and its affiliates.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//


#pragma once

#include <pollux/common/memory/memory.h>

namespace kumo::pollux::io {
    constexpr uint64_t DEFAULT_AUTO_PRELOAD_SIZE =
            (static_cast<uint64_t>((1ul << 20) * 72));

    /**
     * Mode for prefetching data.
     *
     * This mode may be ignored for a reader, such as DWRF, where it does not
     * make sense.
     *
     * To enable single-buffered reading, using the default autoPreloadLength:
     *         ReaderOptions readerOpts;
     *         readerOpts.setPrefetchMode(PrefetchMode::PRELOAD);
     * To enable double-buffered reading, using the default autoPreloadLength:
     *         ReaderOptions readerOpts;
     *         readerOpts.setPrefetchMode(PrefetchMode::PREFETCH);
     * To select unbuffered reading:
     *         ReaderOptions readerOpts;
     *         readerOpts.setPrefetchMode(PrefetchMode::NOT_SET);
     *
     * Single-buffered reading (as in dwio::PreloadableInputStream)
     * reads ahead into a buffer.   Double-buffered reading additionally reads
     * asynchronously into a second buffer, swaps the buffers when the
     * first is fully consumed and the second has been filled, and then starts
     * a new parallel read.  For clients with a slow network connection to
     * Warm Storage, enabling PREFETCH reduces elapsed time by 10% or more,
     * at the cost of a second buffer.   The relative improvement would be greater
     * for cases where the network throughput is higher.
     */
    enum class PrefetchMode {
        NOT_SET = 0,
        PRELOAD = 1, // read a buffer of autoPreloadLength bytes on a read beyond the
        // current buffer, if any.
        PREFETCH = 2, // read a second buffer of autoPreloadLength bytes ahead of
        // actual reads.
    };

    class ReaderOptions {
    public:
        static constexpr int32_t kDefaultLoadQuantum = 8 << 20; // 8MB
        static constexpr int32_t kDefaultCoalesceDistance = 512 << 10; // 512K
        static constexpr int32_t kDefaultCoalesceBytes = 128 << 20; // 128M
        static constexpr int32_t kDefaultPrefetchRowGroups = 1;

        explicit ReaderOptions(pollux::memory::MemoryPool *pool)
            : memoryPool_(pool),
              autoPreloadLength_(DEFAULT_AUTO_PRELOAD_SIZE),
              prefetchMode_(PrefetchMode::PREFETCH) {
        }

        /// Sets the memory pool for allocation.
        ReaderOptions &setMemoryPool(pollux::memory::MemoryPool &pool) {
            memoryPool_ = &pool;
            return *this;
        }

        /// Modifies the autoPreloadLength
        ReaderOptions &setAutoPreloadLength(uint64_t len) {
            autoPreloadLength_ = len;
            return *this;
        }

        /// Modifies the prefetch mode.
        ReaderOptions &setPrefetchMode(PrefetchMode mode) {
            prefetchMode_ = mode;
            return *this;
        }

        /// Modifies the load quantum.
        ReaderOptions &setLoadQuantum(int32_t quantum) {
            loadQuantum_ = quantum;
            return *this;
        }

        /// Modifies the maximum load coalesce distance.
        ReaderOptions &setMaxCoalesceDistance(int32_t distance) {
            maxCoalesceDistance_ = distance;
            return *this;
        }

        /// Modifies the maximum load coalesce bytes.
        ReaderOptions &setMaxCoalesceBytes(int64_t bytes) {
            maxCoalesceBytes_ = bytes;
            return *this;
        }

        /// Modifies the number of row groups to prefetch.
        ReaderOptions &setPrefetchRowGroups(int32_t numPrefetch) {
            prefetchRowGroups_ = numPrefetch;
            return *this;
        }

        /// Gets the memory allocator.
        pollux::memory::MemoryPool &memoryPool() const {
            return *memoryPool_;
        }

        uint64_t autoPreloadLength() const {
            return autoPreloadLength_;
        }

        PrefetchMode prefetchMode() const {
            return prefetchMode_;
        }

        int32_t loadQuantum() const {
            return loadQuantum_;
        }

        int32_t maxCoalesceDistance() const {
            return maxCoalesceDistance_;
        }

        int64_t maxCoalesceBytes() const {
            return maxCoalesceBytes_;
        }

        int64_t prefetchRowGroups() const {
            return prefetchRowGroups_;
        }

        bool noCacheRetention() const {
            return noCacheRetention_;
        }

        void setNoCacheRetention(bool noCacheRetention) {
            noCacheRetention_ = noCacheRetention;
        }

    protected:
        pollux::memory::MemoryPool *memoryPool_;
        uint64_t autoPreloadLength_;
        PrefetchMode prefetchMode_;
        int32_t loadQuantum_{kDefaultLoadQuantum};
        int32_t maxCoalesceDistance_{kDefaultCoalesceDistance};
        int64_t maxCoalesceBytes_{kDefaultCoalesceBytes};
        int32_t prefetchRowGroups_{kDefaultPrefetchRowGroups};
        bool noCacheRetention_{false};
    };
} // namespace kumo::pollux::io
